package MtG.client;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import MtG.client.panels.CardPanel;
import MtG.client.panels.GamePanel;
import MtG.client.panels.InfoPanel;
import MtG.publicClass.Game;
import MtG.publicClass.Message;
import MtG.publicClass.Player;

/**
 * ClientConnection.java is the main class for launching the client- side application.
 * @author Marek Sirp
 * @author Jaan Sepp
 */
public class ClientConnection extends Thread{
	
	/**
	 * Field that holds the socket this client is using.
	 */
	private Socket sock;
	/**
	 * Field that holds the stream for objects that are inbound.
	 */
	private ObjectInputStream netIn;
	/**
	 * Field that holds the stream for objects that are outbound.
	 */
	private ObjectOutputStream netOut;

    /**
     * Field that holds the reference to the opposing Player.
     */
    private Player dataPlayer;
    /**
     * Field that holds the reference to the current game.
     */
    private Game game = null;
    /**
     * Field that holds the reference to this clients cardPanel.
     */
    private CardPanel cardPanel;
    /**
     * Field that holds the reference to this clients user (Player).
     */
    private Player realPlayer;
    /**
     * Field that holds the reference to this clients gamePanel.
     */
    private InfoPanel infoPanel;
    /**
     * Field that holds the reference to this clients gamePanel.
     */
    private GamePanel gamePanel;
    /**
     * boolean to check if the game has ended (unexpectedly).
     */
    private boolean running = true;
    /**
     * the address of the server that is to be connected to.
     */
    private String serverAddress;
	/**
	 * Port used for connecting to the server.
	 */
	private int port = 8899;
    
    
    /**
     * @param p player
     * @param cardPanel the cardPanel of this clients GUI.
     * @param infoPanel the infoPanel of this clients GUI.
     * @param gamePanel the gamePanel of this clients GUI.
     * @param serverAddress 
     */
    public ClientConnection(Player p, CardPanel cardPanel, InfoPanel infoPanel, GamePanel gamePanel, String serverAddress){
    	this.dataPlayer = p;
    	this.realPlayer = p;
    	this.cardPanel = cardPanel;
    	this.infoPanel = infoPanel;
    	this.gamePanel = gamePanel;
    	this.serverAddress = serverAddress;
    }
	
	
	/**
	 * @throws IOException because this method deals with input and output streams which can be unstable.
	 * @throws ClassNotFoundException inbound objects might not be instances of the classes we expect them to be.
	 */
	public void connection() throws IOException, ClassNotFoundException {
		gamePanel.getServerArea().addMessage("Connecting to " + serverAddress + ":" + port);
		InetAddress serverAddr = InetAddress.getByName(serverAddress);
		sock = new Socket(serverAddr, port);			
        netOut = new ObjectOutputStream(sock.getOutputStream());
        netIn = new ObjectInputStream(sock.getInputStream());
        sendObject(realPlayer);
        gamePanel.getServerArea().addMessage("Connection established");
        while(running){
            Object temp = netIn.readObject();  
        	if (temp == null){
        		reset();
    		} else if (temp instanceof Message) {
        		gamePanel.getServerArea().addMessage(temp.toString());
    		} else if(temp instanceof Game){
        		game = (Game)temp;
        		infoPanel.updateStats(game);
        		realPlayer = game.updatePlayer(realPlayer);
        		infoPanel.updateOnTurn(game.getWhoseTurn(), game.playerRoll(game.getWhoseTurn()));
        		cardPanel.placeDeck(realPlayer);
        		if(realPlayer.isSpectator()) {
        			gamePanel.placeCardstoPlay(game.getPlayer1() , game.playerRoll(game.getPlayer1()));
        			gamePanel.placeCardstoPlay(game.getPlayer2() , game.playerRoll(game.getPlayer2()));
        		} else {
        			gamePanel.placeCardstoPlay(game.getWhoseTurn() , game.playerRoll(game.getWhoseTurn()));
        		}
        	} else if (temp instanceof Player){  
        		dataPlayer = (Player)temp;
        		if(dataPlayer.getName().equals("")){
        			continue;
        		}
        		if(dataPlayer.getSecurityNumber().equals(realPlayer.getSecurityNumber())){
        			realPlayer = dataPlayer;       			
        		}
        		infoPanel.updateOnTurn(dataPlayer, game.playerRoll(dataPlayer));
        		cardPanel.placeDeck(realPlayer);
        		gamePanel.placeCardstoPlay(dataPlayer , game.playerRoll(dataPlayer));    
        		if(game.onTurn(realPlayer)){
        			game.update(realPlayer);
        		}
        	}     
        }
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Thread#run()
	 */
	public void run() {
		try {
			try{
			connection();
			} catch (IOException e) {
        		reset();
			}
			if(sock != null){
				gamePanel.getServerArea().addMessage("Disconnected from " + serverAddress + ":" + port);
				netOut.close();
				netIn.close();
			}
		} catch (IOException e1) {
			gamePanel.getServerArea().addMessage("Failed to establish a connection to " + serverAddress + ":" + port);
		} catch (ClassNotFoundException e1) {
			gamePanel.getServerArea().addMessage("Failed to send a object ");
		}
	}
	
	
	/**
	 * Disconnect safely from the server by closing the socket.
	 */
	public void disconnect() {
		if(sock != null){
			sendObject(null);
			try {
				sock.close();
			} catch (IOException e) {
				gamePanel.getServerArea().addMessage("Failed to disconnect socket connection.");
			}
		}
	}
	
	
	/**
	 * @param object object to be sent through the output stream.
	 */
	public void sendObject(Object object) {
		try {
			netOut.writeObject(object);
			netOut.flush();
			netOut.reset();
		} catch (IOException e) {
			gamePanel.getServerArea().addMessage("Objekti saatmine ebaõnnestus");
		}
		
	}
	
    /**
     * @return retrieve reference to the player that uses this client.
     */
    public Player getRealPlayer(){
    	return realPlayer;
    }
    
    /**
     * @param player the player that is using this client.
     */
    public void setRealPlayer(Player player){
    	this.realPlayer = player;
    }
    
    /**
     * @return this game object.
     */
    public Game getGame(){
    	return game;
    }

	/**
	 * Sends the game object to server (ends this players turn).
	 */
	public void switchplayers() {
		sendObject(game);	
	}
	
	/**
	 * reset the clientConnection.
	 */
	public void reset(){
		running = false;
		game = null;
		realPlayer.playerReset();
		cardPanel.placeDeck(realPlayer);
		gamePanel.placeCardstoPlay(realPlayer , "");   		
	}
}
